using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.XPath;

namespace OpenSocial.Net.Atom
{
	public abstract class AtomBuilder
	{
		internal protected void BuildAtomEntry(string atom, AtomEntry entry)
		{
			XmlDocument doc = GetXmlDoc(atom);
			XmlNamespaceManager ns = GetNamespaceManager(doc);
			XPathNavigator nav = doc.CreateNavigator();
			BuildAtomEntry(entry, nav, ns);
		}

		internal protected void BuildAtomEntry(AtomEntry entry, XPathNavigator nav, XmlNamespaceManager ns)
		{
			XPathNavigator node = GetEntryNode("id", ns, nav);
			if (node != null)
			{
				entry.ID = node.InnerXml;
			}

			node = GetEntryNode("summary", ns, nav);
			if (node != null)
			{
				entry.Summary = node.InnerXml;
			}

			node = GetEntryNode("title", ns, nav);
			if (node != null)
			{
				entry.Title = node.InnerXml;
			}

			node = GetEntryNode("updated", ns, nav);
			if (node != null)
			{
				entry.Updated = DateTime.Parse(node.InnerXml);
			}

			//add all links using link position for the key if "rel" isn't present
			XPathNodeIterator iter = GetEntryNodes("link", ns, nav);
			while (iter.MoveNext())
			{
				Link link = new Link();
				link.Href = iter.Current.GetAttribute("href", "");
				link.MimeType = iter.Current.GetAttribute("type", "");
				link.Rel = iter.Current.GetAttribute("rel", "");
				string key = string.IsNullOrEmpty(link.Rel) ? iter.CurrentPosition.ToString() : link.Rel;
				entry.Links.Add(key, link);
			}
		}

		internal protected void BuildAtomFeedBase(string atom, AtomBase feed)
		{
			XmlDocument doc = GetXmlDoc(atom);
			XmlNamespaceManager ns = GetNamespaceManager(doc);
			XPathNavigator nav = doc.CreateNavigator();
			BuildAtomFeedBase(feed, nav, ns);
		}

		internal protected void BuildAtomFeedBase(AtomBase feed, XPathNavigator nav, XmlNamespaceManager ns)
		{
			XPathNavigator node = GetFeedNode("id", ns, nav);
			if (node != null)
			{
				feed.ID = node.InnerXml;
			}

			node = GetFeedNode("title", ns, nav);
			if (node != null)
			{
				feed.Title = node.InnerXml;
			}

			node = GetFeedNode("updated", ns, nav);

			if (node != null)
			{
				feed.Updated = DateTime.Parse(node.InnerXml);
			}
		}

		internal protected XPathNavigator GetEntryNode(string nodeName, XmlNamespaceManager nsMan, XPathNavigator nav)
		{
			string searchPathA = string.Format("/atom:entry/atom:{0}", nodeName);
			string searchPathB = string.Format("atom:{0}", nodeName);
			return nav.SelectSingleNode(searchPathA, nsMan) ?? nav.SelectSingleNode(searchPathB, nsMan);
		}

		internal protected XPathNodeIterator GetEntryNodes(string nodeName, XmlNamespaceManager nsMan, XPathNavigator nav)
		{
			string searchPathA = string.Format("/atom:entry/atom:{0}", nodeName);
			XPathExpression expr1 = nav.Compile(searchPathA);
			expr1.SetContext(nsMan);
			XPathNodeIterator linkIter = nav.Select(expr1);
			if (linkIter.Count == 0)
			{
				string searchPathB = string.Format("atom:{0}", nodeName);
				XPathExpression expr2 = nav.Compile(searchPathB);
				expr2.SetContext(nsMan);
				linkIter = nav.Select(expr2);
			}
			return linkIter;
		}

		internal protected XPathNavigator GetFeedNode(string nodeName, XmlNamespaceManager nsMan, XPathNavigator nav)
		{
			string searchPathA = string.Format("/atom:feed/atom:{0}", nodeName);
			string searchPathB = string.Format("atom:{0}", nodeName);
			return nav.SelectSingleNode(searchPathA, nsMan) ?? nav.SelectSingleNode(searchPathB, nsMan);
		}

		internal protected XPathNodeIterator GetFeedNodes(string nodeName, XmlNamespaceManager nsMan, XPathNavigator nav)
		{
			string searchPathA = string.Format("/atom:feed/atom:{0}", nodeName);
			XPathExpression expr1 = nav.Compile(searchPathA);
			expr1.SetContext(nsMan);
			XPathNodeIterator iter = nav.Select(expr1);
			return iter;
		}

		/// <summary>
		/// Gets a namespace manager for parsing Atom data.
		/// </summary>
		/// <param name="doc">The doc.</param>
		/// <returns></returns>
		internal protected XmlNamespaceManager GetNamespaceManager(XmlDocument doc)
		{
			XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
			ns.AddNamespace("atom", "http://www.w3.org/2005/Atom");
			return ns;
		}
		
		internal protected XPathNodeIterator GetNodes(string atom, string nodePath)
		{
			XmlDocument doc = GetXmlDoc(atom);
			XmlNamespaceManager nsMan = GetNamespaceManager(doc);
			XPathNavigator nav = doc.CreateNavigator();
			XPathExpression expr1 = nav.Compile(nodePath);
			expr1.SetContext(nsMan);
			XPathNodeIterator iter = nav.Select(expr1);
			return iter;
		}

		/// <summary>
		/// Gets an XmlDocument from the given xml string.
		/// </summary>
		/// <param name="xml">The XML.</param>
		/// <returns></returns>
		internal protected XmlDocument GetXmlDoc(string xml)
		{
			XmlDocument doc = new XmlDocument();
			doc.PreserveWhitespace = false;
			doc.LoadXml(xml.Trim());
			return doc;
		}
	}
}
